//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include "aura.h"
#include "wince.h"
#include "private.h"

typedef struct {
    PAuraThreadMain m_pUserEntry;
    Aura_PVoid m_pUserArgs;
}ThreadParams;

static
DWORD WINAPI AuraThreadRoutine(LPVOID pThreadParams)
{
    ThreadParams * pParams = (ThreadParams *)pThreadParams;
    PAuraThreadMain pEntry = pParams->m_pUserEntry;
    Aura_PVoid pUsrArgs = pParams->m_pUserArgs;

    Aura_free(pThreadParams);
    return pEntry(pUsrArgs);
}

AURA_API AuraThread_Create(
        /* [in] */ PAuraThreadMain pEntry,
        /* [in] */ Aura_PVoid pArguments,
        /* [in] */ Aura_ThreadCreationFlags creationFlags,
        /* [out] */ Aura_Handle * phThread,
        /* [out] */ Aura_ThreadId * pThreadId)
{
    ThreadParams * pParams = NULL;
    HANDLE hThread  = NULL;
    DWORD dwCreateFlags = 0;
    DWORD threadId = 0;
    Aura_ECode ec = AURA_NOERROR;

    assert(NULL != phThread);
    assert(NULL != pThreadId);

    pParams = (ThreadParams *)Aura_malloc(sizeof(ThreadParams));
    if (NULL == pParams) {
        ec = AURA_E_OUT_OF_MEMORY;
        goto E_FAIL_EXIT;
    }
    pParams->m_pUserEntry = pEntry;
    pParams->m_pUserArgs  = pArguments;

    if (creationFlags & Aura_ThreadCreationFlag_Unstarted) {
        dwCreateFlags = CREATE_SUSPENDED;
    }
    hThread = CreateThread(NULL, 0,
                        AuraThreadRoutine,
                        pParams,
                        dwCreateFlags,
                        &threadId);
    if (NULL == hThread) {
        ec = GetLastAuraECode();
        goto E_FAIL_EXIT;
    }
    *phThread  = hThread;
    *pThreadId = threadId;

    return AURA_NOERROR;

E_FAIL_EXIT:
    if (NULL != pParams) {
        Aura_free(pParams);
    }
    return ec;
}

AURA_API AuraThread_Resume(
        /* [in] */ Aura_Handle hThread)
{
    DWORD dwRes = 0;

    assert(NULL != hThread);

    dwRes = ResumeThread(hThread);
    if ((DWORD)-1 == dwRes) {
        goto E_FAIL_EXIT;
    }

    return AURA_NOERROR;

E_FAIL_EXIT:
    return GetLastAuraECode();
}

AURA_API AuraThread_Suspend(
        /* [in] */ Aura_Handle hThread)
{
    DWORD dwRes = 0;

    assert(NULL != hThread);

    dwRes = SuspendThread(hThread);
    if ((DWORD)-1 == dwRes) {
        goto E_FAIL_EXIT;
    }

    return AURA_NOERROR;

E_FAIL_EXIT:
    return GetLastAuraECode();
}

AURA_API AuraThread_Join(
        /* [in] */ Aura_Handle hThread,
        /* [in] */ Aura_Millisecond32 timeOut,
        /* [out] */ Aura_WaitResult * pResult)
{
    DWORD dwRes = 0;

    assert(NULL != hThread);
    assert(NULL != pResult);

    dwRes = WaitForSingleObject(hThread,  (DWORD)timeOut);
    if (WAIT_FAILED == dwRes) {
        goto E_FAIL_EXIT;
    }
    else if (WAIT_OBJECT_0 == dwRes) {
        *pResult = Aura_WaitResult_OK;
    }
    else if (WAIT_TIMEOUT == dwRes) {
        *pResult = Aura_WaitResult_TimedOut;
    }
#if _DEBUG
    else {
        assert(0);
    }
#endif

    return AURA_NOERROR;

E_FAIL_EXIT:
    return GetLastAuraECode();
}

AURA_API AuraThread_Abort(
        /* [in] */ Aura_Handle hThread,
        /* [in] */ Aura_QuitCode quitCode)
{
    BOOL bSuccess = FALSE;

    assert(NULL != hThread);

    bSuccess = TerminateThread(hThread, (DWORD)quitCode);
    if (!bSuccess) {
        goto E_FAIL_EXIT;
    }

    return AURA_NOERROR;

E_FAIL_EXIT:
    return GetLastAuraECode();
}

AURA_API AuraThread_Interrupt(
        /* [in] */ Aura_Handle hThread)
{
    FixMe("Not Supported.");
    return AURA_E_NOT_SUPPORTED;
}

AURA_API AuraThread_Destroy(
        /* [in] */ Aura_Handle hThread)
{
    BOOL bSuccess = FALSE;

    assert(NULL != hThread);
    bSuccess = CloseHandle(hThread);
    if (!bSuccess) {
        goto E_FAIL_EXIT;
    }
    return AURA_NOERROR;

E_FAIL_EXIT:
    return GetLastAuraECode();
}

AURA_API AuraThread_GetPriority(
        /* [in] */ Aura_Handle hThread,
        /* [out] */ Aura_ThreadPriority * pPriority)
{
    INT priority = 0;

    assert(NULL != hThread);
    assert(NULL != pPriority);

    priority = GetThreadPriority(hThread);
    if (THREAD_PRIORITY_ERROR_RETURN == priority) {
        goto E_FAIL_EXIT;
    }

    *pPriority = ConvertToAuraPriority(priority);
    return AURA_NOERROR;

E_FAIL_EXIT:
    return GetLastAuraECode();
}

AURA_API AuraThread_SetPriority(
        /* [in] */ Aura_Handle hThread,
        /* [in] */ Aura_ThreadPriority priority)
{
    INT winPriority = 0;
    BOOL bSuccess = FALSE;

    assert(NULL != hThread);

    winPriority = ConvertToWinPriority(priority);
    bSuccess = SetThreadPriority(hThread, winPriority);
    if (!bSuccess) {
        goto E_FAIL_EXIT;
    }

    return AURA_NOERROR;

E_FAIL_EXIT:
    return GetLastAuraECode();
}

AURA_API AuraThread_GetProcessorTimes(
        /* [in] */ Aura_Handle hThread,
        /* [out] */ Aura_Millisecond32 * pUserTime,
        /* [out] */ Aura_Millisecond32 * pPrivilegedTime)
{
    BOOL bSuccess = FALSE;
    FILETIME time[4];

    assert(NULL != hThread);
    assert((NULL != pUserTime) || (NULL != pPrivilegedTime));

    bSuccess = GetThreadTimes(hThread,
                           &time[0], &time[1],
                           &time[2], &time[3]);
    if (!bSuccess) {
        goto E_FAIL_EXIT;
    }

    if (NULL != pUserTime) {
        *pUserTime = ConvertToAuraMilliseconds(&time[3]);
    }
    if (NULL != pPrivilegedTime) {
        *pPrivilegedTime = ConvertToAuraMilliseconds(&time[2]);
    }

    return AURA_NOERROR;

E_FAIL_EXIT:
    return GetLastAuraECode();
}

AURA_API_(Aura_Void) AuraThread_Sleep(
        /* [in] */ Aura_Millisecond32 timeOut,
        /* [out] */ Aura_WaitResult * pResult)
{
    assert(NULL != pResult);

    Sleep((DWORD)timeOut);
    *pResult = Aura_WaitResult_TimedOut;
}

AURA_API_(Aura_Void) AuraThread_Yield()
{
    Sleep(0);
}

AURA_API_(Aura_Void) AuraThread_Quit(
        /* [in] */ Aura_QuitCode quitCode)
{
    ExitThread((DWORD)quitCode);

    assert(0);
}

AURA_API_(Aura_Handle) AuraThread_GetCurrent()
{
    //BUGBUG: Suppose to call GetCurrentThread() API, but the result
    // always be 0x41(main thread handle value).
    // we don't know why yet.
    return (Aura_Handle)GetCurrentThreadId();
}

AURA_API_(Aura_ThreadId) AuraThread_GetCurrentId()
{
    return GetCurrentThreadId();
}

